package com.jake.xfeed.recyclerfeed;

import android.graphics.Rect;
import android.util.Log;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;

import androidx.annotation.NonNull;

import java.lang.ref.WeakReference;

/**
 * 主要用于解决touch时机冲突
 */
public class TouchEventDeterminer {
    public static ParentHelper ontainParentHelper() {
        return new ParentHelper();
    }

    public static void childAttachToParent(@NonNull IChild view) {
        ChildHelper.attachToParent(view);
    }

    public static class ChildHelper {
        public static void attachToParent(@NonNull IChild view) {
            view.getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
                @Override
                public void onViewAttachedToWindow(View v) {
                    IParent parent = findParent(v, IParent.class);
                    if (parent != null) {
                        parent.addChildDeterminer(view);
                    }
                }

                @Override
                public void onViewDetachedFromWindow(View v) {
                    IParent parent = findParent(v, IParent.class);
                    if (parent != null) {
                        parent.removeChildDeterminer(view);
                    }
                }
            });
        }

        public static <T> T findParent(Object viewObj, Class<T> parentCheckType) {
            if (viewObj == null || parentCheckType == null || !(viewObj instanceof View)) {
                return null;
            }
            View view = (View) viewObj;
            ViewParent parent = view.getParent();
            while (parent instanceof ViewGroup) {
                if (parentCheckType.isInstance(parent)) {
                    return (T) parent;
                }
                parent = parent.getParent();
            }
            return null;
        }
    }

    public interface SuperDispatchTouchEventListener {
        boolean dispatchTouchEvent(@NonNull MotionEvent ev);
    }

    public static class ParentHelper {
        private static final String TAG = "TouchDeterminer";
        private SparseArray<WeakReference<IChild>> mChildren = new SparseArray<>();
        private View mTargetView;
        private boolean mIsTouchEventLocked;
        private boolean mContinueTouchWhenChildStop = true;

        public boolean dispatchTouchEvent(@NonNull ViewGroup parent, @NonNull MotionEvent ev, @NonNull SuperDispatchTouchEventListener dispatchTouchEventListener) {
            final int action = ev.getAction();
            if (action == MotionEvent.ACTION_DOWN) {
                mIsTouchEventLocked = checkChildWantsIt(parent, ev);
            }
            boolean result = false;
            if ((null != mTargetView)) {
                result = mTargetView.dispatchTouchEvent(ev);
                Log.e(TAG, "mTargetView.dispatchTouchEvent(ev), Action: " + action + ", TabPager is result=." + result + "    parent=" + parent.hashCode());
                if (!result && mContinueTouchWhenChildStop
                        && (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE)) {
                    mIsTouchEventLocked = false;
                    resetTargetView();
                    MotionEvent fakeEvent = MotionEvent.obtain(ev);
                    fakeEvent.setAction(MotionEvent.ACTION_DOWN);
                    result = dispatchTouchEventListener.dispatchTouchEvent(fakeEvent);
                }
            } else {
                result = dispatchTouchEventListener.dispatchTouchEvent(ev);
            }
            Log.d(TAG, "mTargetView.dispatchTouchEvent(ev), Action: " + action + ", TabPager is result=." + result + "    parent=" + parent.hashCode());
            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
                resetTargetView();
            }
            return result;
        }

        public boolean isTouchEventLocked() {
            return mIsTouchEventLocked;
        }

        private void resetTargetView() {
            if (mTargetView != null) {
                Log.d(TAG, "dispatchTouchEvent, TabPager is Unlocked.");
                mIsTouchEventLocked = false;
                mTargetView = null;
            }
        }


        private boolean checkChildWantsIt(@NonNull ViewGroup parent, @NonNull MotionEvent ev) {
            if (mTargetView != null) {
                mTargetView = null;
            }
            final int x = (int) ev.getX();
            final int y = (int) ev.getY();
            Rect hitRect = new Rect();
            for (int i = 0; i < mChildren.size(); i++) {
                int key = mChildren.keyAt(i);
                WeakReference<IChild> ref = mChildren.get(key);
                if (ref != null && ref.get() != null) {
                    final IChild child = ref.get();
                    final View childView = child.getView();
                    if (childView.getVisibility() == View.VISIBLE) {
                        int realX = x;
                        int realY = y;
                        int[] offset = new int[2];
                        if (!getTargetViewOffset(parent, childView, offset)) {
                            continue;
                        }
                        realX += offset[0];
                        realY += offset[1];
                        childView.getHitRect(hitRect);
                        if (hitRect.contains(realX, realY)) {
                            if (child.determineTouchEvent(ev)) {
                                mTargetView = childView;
                                Log.d(TAG, "checkIfSpecialChildWantsIt, Child: " + childView.getClass().toString() + " wants it.");
                                return true;
                            }
                        }
                    }
                }
            }
            Log.d(TAG, "checkIfSpecialChildWantsIt, NO ONE wants it.");
            return false;
        }

        private boolean getTargetViewOffset(@NonNull View parentView, @NonNull View targetView, @NonNull int[] location) {
            location[0] = 0;
            location[1] = 0;
            Log.d(TAG, "getTargetViewOffset, Target: " + targetView);
            if (!(targetView.getParent() instanceof View)) {
                return false;
            }
            View parent = (View) targetView.getParent();
            while (parent != null && parent != parentView) {
                Log.d(TAG, "getTargetViewOffset, Parent: " + parent);
                location[0] += parent.getScrollX() - parent.getLeft();
                location[1] += parent.getScrollY() - parent.getTop();

                final ViewParent viewParent = parent.getParent();
                if (viewParent instanceof View) {
                    parent = (View) viewParent;
                } else {
                    parent = null;
                }
            }

            if (parent == parentView) {
                location[0] += parentView.getScrollX();
                location[1] += parentView.getScrollY();
                return true;
            } else {
                Log.w(TAG, "targetView is not inside TabPager! Probably it's inside a listView and currently not visible.");
                return false;
            }
        }

        public void addChildDeterminer(IChild child) {
            if (child != null) {
                mChildren.put(child.hashCode(), new WeakReference<>(child));
            }
        }

        public void removeChildDeterminer(IChild child) {
            if (child != null) {
                mChildren.remove(child.hashCode());
            }
        }

        public void setContinueTouchWhenChildStop(boolean enable) {
            this.mContinueTouchWhenChildStop = enable;
        }
    }

    public interface IParent {
        void addChildDeterminer(IChild child);

        void removeChildDeterminer(IChild child);
    }

    public interface IChild {
        boolean determineTouchEvent(MotionEvent ev);

        @NonNull
        View getView();
    }
}
